/********************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2006-2008.
---------------------------------------------------------------------
File name:   PS3PadWin.cpp
Description: JoyPad for PS3 in Windows
---------------------------------------------------------------------
History:
- 05:05:2008 : Created by Kevin Kirst

*********************************************************************/

#include "StdAfx.h"

#include "PS3PadWin.h"

#if CRY_USE_PS3PADWIN

#pragma comment (lib, "libpad")

const char* ps3DeviceNames[] =
{
	"pad0",
	"pad1",
	"pad2",
	"pad3"
};

//////////////////////////////////////////////////////////////////////////
CPS3PadWin::CPS3PadWin(IInput& pInput, int deviceNo) : CInputDevice(pInput, ps3DeviceNames[deviceNo]),
	m_deviceNo(deviceNo), m_connected(false)
{
	m_deviceId = eDI_XI;
	memset(m_buttons, 0, sizeof(m_buttons));
	memset(m_buttonsLast, 0, sizeof(m_buttonsLast));
	m_xAxis[0] = m_xAxis[1] = m_xAxis[2] = m_yAxis[0] = m_yAxis[1] = m_yAxis[2] = m_zAxis = m_gyro  = 0;
}

//////////////////////////////////////////////////////////////////////////
CPS3PadWin::~CPS3PadWin()
{

}

//////////////////////////////////////////////////////////////////////////
bool CPS3PadWin::InitLib()
{
	// init pad library
	const int RetInit = cellPadInit(1);
	if (RetInit != CELL_PAD_OK)
	{
		gEnv->pLog->Log("Error: Initializing Cell USB Pad library\n");
		return false;
	}

#if defined(CRY_USE_GCM_HUD)
	if (RetInit != CELL_PAD_ERROR_ALREADY_INITIALIZED)
	{
		gEnv->pLog->Log("Error: Initializing Cell USB Pad library\n");
		return false;
	}
#endif

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CPS3PadWin::KillLib()
{
	cellPadEnd();
}

//////////////////////////////////////////////////////////////////////////
bool CPS3PadWin::Init()
{
#if defined(USE_LEAN_HACK)
	m_rotXLActive = m_rotXRActive = m_rotZDActive = m_rotZUActive = false;
#endif

	MapSymbol(PS3_BUTTON_SELECT, eKI_PS3_Select, "pad_select");
	MapSymbol(PS3_BUTTON_L3, eKI_PS3_L3, "pad_l3");
	MapSymbol(PS3_BUTTON_R3, eKI_PS3_R3, "pad_r3");
	MapSymbol(PS3_BUTTON_START, eKI_PS3_Start, "pad_start");
	MapSymbol(PS3_BUTTON_UP, eKI_PS3_Up, "pad_up");
	MapSymbol(PS3_BUTTON_RIGHT, eKI_PS3_Right, "pad_right");
	MapSymbol(PS3_BUTTON_DOWN, eKI_PS3_Down, "pad_down");
	MapSymbol(PS3_BUTTON_LEFT, eKI_PS3_Left, "pad_left");
	MapSymbol(PS3_BUTTON_L2, eKI_PS3_L2, "pad_l2");
	MapSymbol(PS3_BUTTON_R2, eKI_PS3_R2, "pad_r2");
	MapSymbol(PS3_BUTTON_L1, eKI_PS3_L1, "pad_l1");
	MapSymbol(PS3_BUTTON_R1, eKI_PS3_R1, "pad_r1");
	MapSymbol(PS3_BUTTON_TRIANGLE, eKI_PS3_Triangle,"pad_triangle");
	MapSymbol(PS3_BUTTON_CIRCLE, eKI_PS3_Circle, "pad_circle");
	MapSymbol(PS3_BUTTON_CROSS, eKI_PS3_Cross, "pad_cross");
	MapSymbol(PS3_BUTTON_SQUARE, eKI_PS3_Square, "pad_square");
	MapSymbol(PS3_STICK_LEFT_X, eKI_PS3_StickLX, "pad_sticklx",	SInputSymbol::Axis);
	MapSymbol(PS3_STICK_LEFT_Y, eKI_PS3_StickLY, "pad_stickly",	SInputSymbol::Axis);
	MapSymbol(PS3_STICK_RIGHT_X, eKI_PS3_StickRX, "pad_stickrx",	SInputSymbol::Axis);
	MapSymbol(PS3_STICK_RIGHT_Y, eKI_PS3_StickRY, "pad_stickry",	SInputSymbol::Axis);
#if defined(USE_LEAN_HACK)
	MapSymbol(PS3_ROTX_KEY_L, eKI_PS3_RotX_KeyL, "pad_rotx_keyl");
	MapSymbol(PS3_ROTX_KEY_R, eKI_PS3_RotX_KeyR, "pad_rotx_keyr");
	MapSymbol(PS3_ROTZ_KEY_D, eKI_PS3_RotZ_KeyD, "pad_rotz_keyd");
	MapSymbol(PS3_ROTZ_KEY_U, eKI_PS3_RotZ_KeyU, "pad_rotz_keyu");
#endif
	MapSymbol(PS3_ROT_X, eKI_PS3_RotX, "pad_rotx",	SInputSymbol::Axis);
	MapSymbol(PS3_ROT_Y, eKI_PS3_RotY, "pad_roty",	SInputSymbol::Axis);
	MapSymbol(PS3_ROT_Z, eKI_PS3_RotZ, "pad_rotz",	SInputSymbol::Axis);

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CPS3PadWin::Update(bool bFocus)
{
	CellPadInfo info;	// connection status
	CellPadData state;	// gamepad data

	// get current status
	if (cellPadGetInfo(&info) == CELL_PAD_OK && bFocus)
	{
		if (!UpdateConnectionState(info.status[m_deviceNo] == 1))
			return;

		if (cellPadGetData(m_deviceNo, &state) == CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD || state.len == 0)
			return;

		for(int i = 0; i < 16; ++i)
		{
			// See PS3Pad.cpp for explination of code. Keep same.
			if(sPadMappings[i * 2 + 4] >= 0)
				m_buttons[i] = (state.button[sPadMappings[i * 2 + 4]] & (1 << sPadMappings[i * 2 + 5])) ? 1 : 0;
			else
				m_buttons[i] = 0;
		}

		SInputEvent event;
		SInputSymbol*	pSymbol = 0;
		for(int i = 0; i < 16; ++i)
		{
			if(m_buttons[i] ^ m_buttonsLast[i])
			{
				if((pSymbol = DevSpecIdToSymbol(i)) != NULL)
				{
					pSymbol->PressEvent(m_buttons[i] != 0);
					pSymbol->AssignTo(event);
					GetIInput().PostInputEvent(event);
				}
			}
			m_buttonsLast[i] = m_buttons[i];
		}
		ReadAxis(state);
	}
}

//////////////////////////////////////////////////////////////////////////
bool CPS3PadWin::UpdateConnectionState(bool current)
{
	if (current != m_connected)
	{
		SInputEvent event;
		if (current)
		{
			//connect
			event.keyName = "connect";
		}
		else
		{
			// disconnect
			event.keyName = "disconnect";
		}
		m_connected = current;
		if(m_connected)
		{
			if (cellPadInfoSensorMode(m_deviceNo) == CELL_PAD_INFO_SUPPORTED_SENSOR_MODE)
				cellPadSetSensorMode(m_deviceNo, CELL_PAD_SENSOR_MODE_ON);
		}
		GetIInput().PostInputEvent(event);
	}

	return m_connected;
}

//////////////////////////////////////////////////////////////////////////
void CPS3PadWin::ReadAxis(const CellPadData& crData)
{
	int	xAxis[3];	//x-axis values
	int	yAxis[3];	//y-axis values
	int zAxis;		//z-axis values
	int	gyro;			// Gyro values

	//read the analog axes
	xAxis[0] = crData.button[sPadMappings[0]]; //range: 256
	yAxis[0] = crData.button[sPadMappings[1]]; //range: 256
	xAxis[1] = crData.button[sPadMappings[2]]; //range: 256
	yAxis[1] = crData.button[sPadMappings[3]]; //range: 256
	xAxis[2] = crData.button[sPadMappings[36]];//range: 1024
	yAxis[2] = crData.button[sPadMappings[37]];//range: 1024
	zAxis		 = crData.button[sPadMappings[38]];//range: 1024
	gyro = crData.button[sPadMappings[39]];		 //range: 1024

	SInputEvent event;
	SInputSymbol*	pSymbol = 0;

	for(int i=0; i<2; ++i)
	{
		//handle PS3_STICK_LEFT_X and (PS3_STICK_LEFT_X+1 => PS3_STICK_RIGHT_X)
		if(xAxis[i] != m_xAxis[i])
		{
			const float cVal = MapPadValue256(xAxis[i]);
			pSymbol = DevSpecIdToSymbol(PS3_STICK_LEFT_X+i);			assert(pSymbol);
			pSymbol->ChangeEvent(cVal);
			pSymbol->AssignTo(event);
			GetIInput().PostInputEvent(event);
		}
	}
	if(yAxis[0] != m_yAxis[0])
	{
		const float cVal = MapPadValue256(yAxis[0]);
		pSymbol = DevSpecIdToSymbol(PS3_STICK_LEFT_Y);			assert(pSymbol);
		pSymbol->ChangeEvent(-cVal);
		pSymbol->AssignTo(event);
		GetIInput().PostInputEvent(event);
	}
	if(yAxis[1] != m_yAxis[1])
	{
		const float cVal = MapPadValue256(yAxis[1]);
		pSymbol = DevSpecIdToSymbol(PS3_STICK_RIGHT_Y);			assert(pSymbol);
		pSymbol->ChangeEvent(-cVal);
		pSymbol->AssignTo(event);
		GetIInput().PostInputEvent(event);
	}
#if defined(USE_LEAN_HACK)
	if(xAxis[2] != m_xAxis[2])
	{
		const float cVal = MapPadValue1024(xAxis[2]);
		if(cVal > LEAN_THRESHOLD)
		{
			if(!m_rotXRActive)
			{
				m_rotXRActive = true;
				pSymbol = DevSpecIdToSymbol(PS3_ROTX_KEY_R);			assert(pSymbol);
				pSymbol->PressEvent(true);
				pSymbol->AssignTo(event);
				GetIInput().PostInputEvent(event);
			}
		}
		else
			if(m_rotXRActive && cVal < LEAN_THRESHOLD_RESET)
			{
				m_rotXRActive = false;
				pSymbol = DevSpecIdToSymbol(PS3_ROTX_KEY_R);			assert(pSymbol);
				pSymbol->PressEvent(false);
				pSymbol->AssignTo(event);
				GetIInput().PostInputEvent(event);
			}
			if(cVal < -LEAN_THRESHOLD)
			{
				if(!m_rotXLActive)
				{
					m_rotXLActive = true;
					pSymbol = DevSpecIdToSymbol(PS3_ROTX_KEY_L);			assert(pSymbol);
					pSymbol->PressEvent(true);
					pSymbol->AssignTo(event);
					GetIInput().PostInputEvent(event);
				}
			}
			else
				if(m_rotXLActive && cVal > -LEAN_THRESHOLD_RESET)
				{
					m_rotXLActive = false;
					pSymbol = DevSpecIdToSymbol(PS3_ROTX_KEY_L);			assert(pSymbol);
					pSymbol->PressEvent(false);
					pSymbol->AssignTo(event);
					GetIInput().PostInputEvent(event);
				}
	}

	if(zAxis != m_zAxis)
	{
		const float cVal = MapPadValue1024(zAxis);
		if(cVal > LEAN_THRESHOLD)
		{
			if(!m_rotZUActive)
			{
				m_rotZUActive = true;
				pSymbol = DevSpecIdToSymbol(PS3_ROTZ_KEY_U);			assert(pSymbol);
				pSymbol->PressEvent(true);
				pSymbol->AssignTo(event);
				GetIInput().PostInputEvent(event);
			}
		}
		else
			if(m_rotZUActive && cVal < LEAN_THRESHOLD_RESET)
			{
				m_rotZUActive = false;
				pSymbol = DevSpecIdToSymbol(PS3_ROTZ_KEY_U);			assert(pSymbol);
				pSymbol->PressEvent(false);
				pSymbol->AssignTo(event);
				GetIInput().PostInputEvent(event);
			}
			if(cVal < -LEAN_THRESHOLD)
			{
				if(!m_rotZDActive)
				{
					m_rotZDActive = true;
					pSymbol = DevSpecIdToSymbol(PS3_ROTZ_KEY_D);			assert(pSymbol);
					pSymbol->PressEvent(true);
					pSymbol->AssignTo(event);
					GetIInput().PostInputEvent(event);
				}
			}
			else
				if(m_rotZDActive && cVal > -LEAN_THRESHOLD_RESET)
				{
					m_rotZDActive = false;
					pSymbol = DevSpecIdToSymbol(PS3_ROTZ_KEY_D);			assert(pSymbol);
					pSymbol->PressEvent(false);
					pSymbol->AssignTo(event);
					GetIInput().PostInputEvent(event);
				}
	}
#endif
	//save values
	m_gyro	= gyro;
	m_xAxis[0] = xAxis[0];	m_xAxis[1] = xAxis[1];	m_xAxis[2] = xAxis[2];
	m_yAxis[0] = yAxis[0];	m_yAxis[1] = yAxis[1];	m_yAxis[2] = yAxis[2];
	m_zAxis		 = zAxis;
}

void CPS3Pad::ClearAnalogKeyState(TInputSymbols& clearedSymbols)
{
	m_xAxis[0] = m_xAxis[1] = m_xAxis[2] = m_yAxis[0] = m_yAxis[1] = m_yAxis[2] = m_zAxis = m_gyro = 0;

	// Also clear L2 and R2 as they may be treated as analog input (for consistancy with x360 gamepad)
	m_buttonsLast[PS3_BUTTON_L2] = 0;
	m_buttonsLast[PS3_BUTTON_R2] = 0;

	//remove the symbols cleared above from the hold list
	static SInputSymbol* pSymbolL2 = DevSpecIdToSymbol(PS3_BUTTON_L2);
	static SInputSymbol* pSymbolR2 = DevSpecIdToSymbol(PS3_BUTTON_R2);

#if defined(USE_LEAN_HACK)
	static SInputSymbol* pSymbolL = DevSpecIdToSymbol(PS3_ROTX_KEY_L);
	static SInputSymbol* pSymbolR = DevSpecIdToSymbol(PS3_ROTX_KEY_R);
	static SInputSymbol* pSymbolU = DevSpecIdToSymbol(PS3_ROTZ_KEY_U);
	static SInputSymbol* pSymbolD = DevSpecIdToSymbol(PS3_ROTZ_KEY_D);

	clearedSymbols.reserve(6);

	clearedSymbols.push_back(pSymbolL);
	clearedSymbols.push_back(pSymbolR);
	clearedSymbols.push_back(pSymbolU);
	clearedSymbols.push_back(pSymbolD);

#else
	clearedSymbols.reserve(2);
#endif

	clearedSymbols.push_back(pSymbolL2);
	clearedSymbols.push_back(pSymbolR2);
}

#endif // #if CRY_USE_PS3PADWIN